import {mat4, vec3, vec4} from "gl-matrix";


export class ShaderProgram{

    vertexShader:WebGLShader;
    fragShader:WebGLShader;
    program:WebGLProgram;

    uniforms = {};
    attribs = {};

    constructor(public gl:WebGLRenderingContext,vsSource:string,fsSource:string) {
        let vs = this.createShader(gl.VERTEX_SHADER,vsSource);
        let fs = this.createShader(gl.FRAGMENT_SHADER,fsSource);
        let program = gl.createProgram()!;
        gl.attachShader(program,vs);
        gl.attachShader(program,fs);
        gl.linkProgram(program);

        let msg = gl.getProgramInfoLog(program);
        if (msg && msg.length > 0){
            throw msg
        }

        gl.detachShader(program,vs);
        gl.detachShader(program,fs);

        this.vertexShader = vs;
        this.fragShader = fs;
        this.program = program;
    }

    private createShader(shaderType:GLenum,shaderSource:string){
        let gl = this.gl;
        let shader = gl.createShader(shaderType)!;
        if (shader <= 0){
            throw new Error(`create shaderType:${shaderType} error!`);
        }
        gl.shaderSource(shader,shaderSource);
        gl.compileShader(shader);

        let msg = gl.getShaderInfoLog(shader);
        if (msg && msg.length > 0){
            throw msg;
        }

        return shader;
    }

    bind(){
        this.gl.useProgram(this.program);
    }

    unbind(){
        this.gl.useProgram(null);
    }

    cleanup(){
        this.unbind();
        let gl = this.gl;
        if (this.program > 0){
            gl.deleteProgram(this.program);
            gl.deleteShader(this.vertexShader);
            gl.deleteShader(this.fragShader);
        }
    }


    createUniform(name:string){
        let gl = this.gl;
        let location = gl.getUniformLocation(this.program,name);
        if (location === null || location < 0){
            throw `can not found uniform :${name}`
        }
        // @ts-ignore
        this.uniforms[name] = location;
    }

    createAttrib(name:string){
        let gl = this.gl;
        let location = gl.getAttribLocation(this.program,name);
        if (location < 0){
            throw `can not found attrib ${name}`
        }

        // @ts-ignore
        this.attribs[name] = location;
    }

    setUniformI(name:string,x:number){
        // @ts-ignore
        this.gl.uniform1i(this.uniforms[name],x);
    }
    setUniformF(name:string,x:number){
        // @ts-ignore
        this.gl.uniform1f(this.uniforms[name],x);
    }
    setUniformVec3(name:string,v:vec3){
        // @ts-ignore
        this.gl.uniform3f(this.uniforms[name],v[0],v[1],v[2]);
    }
    setUniformVec4(name:string,v:vec4){
        // @ts-ignore
        this.gl.uniform4f(this.uniforms[name],v[0],v[1],v[2],v[3]);
    }
    setUniformMat4(name:string,m4:mat4){
        // @ts-ignore
        this.gl.uniformMatrix4fv(this.uniforms[name],false,m4);
    }

    getAttribId(name:string){
        // @ts-ignore
        return this.attribs[name]
    }

}
